Extensions C

參考

參考-1

基本介紹

在 C 語言中有一些 Extensions 的方法

要使用這些 Extensions 功能你可以先檢查 GNUC 是否有開

這些 Extensions 不是標準 C 的功能,是需要編譯器支援的

1
2
3
4
5
#if __GNUC__
/*
有定義 __GNUC__ 才可以使用 Extensions
*/
#endif

如果在 GCC 編譯時加上 -pedantic

而且你有用 Extensions 的話會發出警告

也就是說當你加上 -pedantic 代表你不想要用 Extensions

Statement Exprs

將語法包在 ({}) 之中,會回傳最後一段的值

如下例子總共有 4 行程式碼,但最後會回傳z的值

1
2
3
4
5
6
7
8
9
10
#define EXPRS   ({  int x = 5; int y=10; int z=0;   \
if (x > y) z = x; \
else z = y; \
z; })

int main(void)
{
printf("%d \n",EXPRS);
return 0;
}

Locally Declared Labels

定義一個 label 常和 goto 搭配

以下是在 while loop 裡面跳出到 labelA

記得被跳的 labelA 的後面要寫 :

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(void)
{
__label__ labelA;
int i = 0;
while(1)
{
goto labelA;
}

labelA:
printf("Fineish! \n");
return 0;
}

Labels as Values

將 Labels 當作變數來使用

在 Labels 前使用 && 可以取得 Labels 位置

使用 goto 就可以跳到 Labels

GCC 建議使用 switch 而不是用 Labels array

除非當 switch 做不到時才考慮用 Labels array 取代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* Ex1 只會顯示 L1 */
int main(void)
{
const void *jmpLabel = &&L1;
goto *jmpLabel;

printf("Hello\n");

L1:
printf("L1\n");
return 0;
}

/* Ex2 只會顯示 L2 (類似 switch)*/
int main(void)
{
const void *jmpLabel[] = {&&L1,&&L2,&&L3};
goto *jmpLabel[1];

L1:
printf("L1\n");
return 0;
L2:
printf("L2\n");
return 0;
L3:
printf("L3\n");
return 0;


return 0;
}

/* Ex3 改寫上述寫法,此寫法在做 shared LIB 時比較好 */
int main(void)
{
static const int jmpLabel[] = {&&L1-&&L1, &&L2-&&L1, &&L3-&&L1};
goto *(&&L1 + jmpLabel[0]);

L1:
printf("L1\n");
return 0;
L2:
printf("L2\n");
return 0;
L3:
printf("L3\n");
return 0;
return 0;
}

Nested Functions

巢狀函示,在函式中定義另一個函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int funcA(int a, int b)
{
// Nested Functions
int square(int c){
return (c * c);
}

return square (a) + square (b);
}

int main(void)
{
printf("%d\n",funcA(2,3)); // 13
return 0;
}

Typeof

回傳參數型態,如下例子typeof(a) 等於取a型態

所以等於 int y;

1
2
3
4
5
6
7
8
9
10
11
12
int main(void)
{
int a = 0;
typeof(a) y;
return 0;
}

// 比大小的例子
#define max(a,b) ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })

// 或是寫成
#define max(a,b) ({ __auto_type _a = (a); __auto_type _b = (b); _a > _b ? _a : _b; })

Conditionals

你可以把

1
x ? x : y

改寫為

1
x ? : y

當你 x 很長的時候比較有用,就不需要寫兩次 x 敘述

128-bit Integers

需要看你的硬體是否有支援

1
2
3
4
5
6
int main(void)
{
__int128 a; // signed 128-bit integer
unsigned __int128 b; // unsigned 128-bit integer
return 0;
}

Double-Word Integers

64 bit 長度型別

1
2
3
4
5
6
int main(void)
{
long long int a;
unsigned long long int b;
return 0;
}

Complex Numbers

數學的複數運算,記得要include <complex.h>

使用複數時要加上ij,例如%.2fi%.2fj

creal()cimag() 可替換為 __real____imag__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <complex.h>

int main(void)
{
double complex z1 = 10.0 + 5.0 * I;
double complex z2 = 10.0 - 3.0 * I;
printf("z1 = %.2f + %.2fi \n", creal(z1), cimag(z1));
printf("z2 = %.2f + %.2fi \n", creal(z2), cimag(z2));

double complex sum = z1 + z2;
printf("Sum: z1 + z2 = %.2f %+.2fi\n", creal(sum), cimag(sum));

double complex difference = z1 - z2;
printf("The difference: Z1 - Z2 = %.2f %+.2fi\n", creal(difference), cimag(difference));

double complex product = z1 * z2;
printf("The product: Z1 x Z2 = %.2f %+.2fi\n", creal(product), cimag(product));

double complex quotient = z1 / z2;
printf("The quotient: Z1 / Z2 = %.2f %+.2fi\n", creal(quotient), cimag(quotient));

double complex conjugate = conj(z1);
printf("The conjugate of Z1 = %.2f %+.2fi\n", creal(conjugate), cimag(conjugate));
return 0;
}

Arrays of Length Zero

此用法在於可以宣告動態大小的 struct,先看傳統用法

1
2
3
4
typedef struct{
int age;
char name[10];
}stPerson;

此 struct 大小就是固定 4 byte + 10 byte

如果用以下寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct{
int age;
char name[0];
}stPerson;

int main(void)
{

// 在配置記憶體時額外加上需要的大小,此例子為 20
// 你也可以改為其他大小,藉此達到改變 struct 的 size
char nameLen = 20;
stPerson *pPerson = malloc( sizeof(stPerson) + nameLen);

// Assign
pPerson->age = 28;
strncpy(pPerson->name, "Jason", strlen("Jason") + 1);
printf("Age = %d\n",pPerson->age); // 28
printf("Name = %s\n",pPerson->name); // Jason

// 這樣就可以利用 name array 存取自行配置的空間
printf("Name[0] = %c\n",pPerson->name[0]); // J
printf("Name[1] = %c\n",pPerson->name[1]); // a
printf("Name[2] = %c\n",pPerson->name[2]); // s
}

額外參考

Macros with a Variable Number of Arguments.

Macros 使用 ... 表示可傳入多個參數

1
2
3
4
5
6
7
#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)
int main(void)
{
debug("%s \n","Hello"); // 1 個參數
debug("%s %s\n","Hello","World"); // 2 個參數
return 0;
}

可以改寫為

1
#define debug(format, args...) fprintf (stderr, format, args)

但以上寫法遇到沒有參數時會有問題,例如

1
2
3
4
5
6
#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)
int main(void)
{
debug("Hi\n");
return 0;
}

所以可以加上 ## 來解決

1
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

## 的其他用法

1
2
3
4
5
6
7
8
9
10
11
#define sayHi(Type) func##Type

void funcA(void){ printf("Hi A\n"); }
void funcB(void){ printf("Hi B\n"); }
void funcC(void){ printf("Hi C\n"); }

int main(void)
{
sayHi(B)(); // Hi B
return 0;
}

Designated Initializers

初始化 array 的值可以使用

1
2
3
4
int a[6] = { [4] = 29, [2] = 15 };

// 等於
int a[6] = { 0, 0, 15, 0, 29, 0 };

也可以用 ... 代表一個範圍

1
int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };

其他用法

1
2
int whitespace[256] = { ['A']  = 1, ['B']  = 1, ['\h'] = 1, 
['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };